#include "mempool.h"

slice slice__New(int64_t len) {
  slice ret;
  ret.p = malloc(len);
  ret.len = 0;
  ret.cap = len;
  return ret;
}

void *slice__At(slice s, int64_t offset, int64_t unit) {
  void *ptr = s.p + offset * unit;
  return ptr;
}

slice slice__grow(slice s, int64_t cap) {
  slice ret = s;
  if (cap * 2 > s.cap) { // 如果要求容量大于2倍
    ret.cap = cap;
    ret.len = s.len;
    ret.p = malloc(cap);
    memcpy(ret.p, s.p, s.len); // 复制旧数据到新数据
    return ret;
  }
  ret.cap = ret.cap * 2;
  ret.len = s.len;
  ret.p = malloc(ret.cap); // 复制旧数据到新数据
  memcpy(ret.p, s.p, s.len);
  return ret;
}

slice slice__append(slice s, void *ptr, int64_t size) {
  memcpy(s.p + s.len, ptr, size);
  s.len += size;
  return s;
}

// mempool__New 创建一个内存池
mempool__Mempool mempool__New(int64_t size) {
  mempool__Mempool ret;
  // 分配内存池地址保存数组
  ret.p = slice__New(sizeof(slice));
  if (size < 0) { // 如果预分配大小小于0，视为预分配长度为0字节内存
    size = 0;
  }

  // 分配内存池容量
  slice ptr;
  ptr.p = malloc(size);
  ptr.len = 0;
  ptr.cap = size;

  ret.p = slice__append(ret.p, &ptr, sizeof(slice));

  // 初始化互斥锁
  if (pthread_mutex_init(&ret.lock, NULL) != 0) {
    fprintf(stderr, "初始化互斥锁失败");
  }
  return ret;
}

// mempool__Mempool_New 从内存池分配一段长度为size的内存
void *mempool__Mempool_New(mempool__Mempool *ptr, int64_t size) {
  pthread_mutex_lock(&(ptr->lock));
  int offset = (ptr->p.len) / sizeof(slice) - 1;
  slice *memptr = slice__At(ptr->p, offset, sizeof(slice));
  int cap = memptr->cap;
  while (memptr->cap < memptr->len + size) { // 如果内存池剩余容量不足
                                             // 分配新连续内存
    ptr->p = slice__grow(ptr->p, (offset + 1) * sizeof(slice));
    offset = (ptr->p.len) / sizeof(slice) - 1;
    memptr = slice__At(ptr->p, offset, sizeof(slice));
    memptr->p = malloc(cap * 2 + 1); // cap 2倍加1是为了防止cap==0
    memptr->len = 0;
    memptr->cap = cap * 2 + 1;
    cap = cap * 2 + 1;
  }
  void *ret = memptr->p + memptr->len;
  memptr->len += size;
  pthread_mutex_unlock(&(ptr->lock));
  return ret;
}

// mempool_Free 释放内存池的所有内存
void mempool_Free(mempool__Mempool *ptr) {
  for (int64_t i = 0; i < (ptr->p.len) / sizeof(slice); i++) {
    slice *memptr = slice__At(ptr->p, i, sizeof(slice));
    free(memptr->p);
  }
  pthread_mutex_destroy(&ptr->lock);
  return;
}